#include <util/twi.h>
#include <devices/twi.h>

/* 当timeout变量为0时，不进行超时设置 */
static unsigned timeout = 0;
static Twi twi;
static void twiTimeout( void );
/* twi结构中的timeout成员表示是否有超时被设置，
 * 操作成功的前提是只使用start/cancel函数来操作twi的超时
 * start函数启动定时器，也就是应该将timeout成员置为真  */
static inline void twiStartTimeout( void )
{
	if( timeout ){
		twi.timeout = True;
		AvrXStartTimerMessage( &twi.msg, timeout, &twi.q );
	} else {
		twi.timeout = False;
	}
}
/* stop函数停止定时器，也就是应该将timeout成员恢复为False */
static inline void twiCancelTimeout( void )
{
	if( twi.timeout ){
		twi.timeout = False;
		AvrXCancelTimerMessage( &twi.msg, &twi.q );
	}
}
/* 设置总线地址，设置波特率，设置管脚（使能上拉电阻） */
void twi_init( uint8_t baud, TwiAddr addr )
{
	cli();
	TWAR = addr << 1;//不接收广播，想接收广播加 | _BV(TWGCE)
	TWBR = baud;
	PORTD |= 0x03;
	DDRD  &= ~0x03;
	sei();
	twi.mode = TwiModeStop;
	AvrXSetSemaphore( &twi.lock );
}

/* 开始发送数据，指定缓冲区，指定数据大小，写START，等待传输结束，返回实际发送的数据数 */
int twi_write( TwiAddr sla, const void *buf, size_t sz )
{
	AvrXWaitSemaphore( &twi.lock );
	twi.target = sla;
	twi.buf.w = buf;
	twi.cnt = sz;
	twi.mode = TwiModeMT;
	TWCR = _BV(TWIE)|_BV(TWINT)|_BV(TWSTA)|_BV(TWEN);
	twiStartTimeout();
	pMessageControlBlock msg = NULL;
	do{
		msg = AvrXWaitMessage( &twi.q );
	}while(msg == NULL );
	if( twi.timeout ){ //尚有未传输的数据，属于超时
		twiTimeout();
	}
	AvrXSetSemaphore( &twi.lock );
	return ( sz - twi.cnt );
}

/* 开始读入数据，设置Twi为从机模式，等传输结束，返回实际读入数据数 */
int twi_read( void *buf, size_t sz )
{
	AvrXWaitSemaphore( &twi.lock );
	twi.buf.r = buf;
	twi.cnt = sz;
	twi.mode = TwiModeSR;
	TWCR = _BV(TWIE)|_BV(TWINT)|_BV(TWEA)|_BV(TWEN);
	twiStartTimeout();
	pMessageControlBlock msg = NULL;
	do{
		msg = AvrXWaitMessage( &twi.q );
	}while(msg == NULL );
	if( twi.timeout ){ //尚有未传输的数据，属于超时
		twiTimeout();
	}
	AvrXSetSemaphore( &twi.lock );
	return ( sz - twi.cnt );
}

/* 最的一部分，当read, write设置好状态之后，中根据当前的状态作出响应，如果传输结束，亦应该作出通知
 * 保存TW_STATUS状态，然后根据状态判断动作
 */
DEF_OSISR( TWI_vect )
{
	IntProlog();
	uint8_t status = TW_STATUS;
	twi.status = status;
	if( twi.mode == TwiModeMT )
	{
		/*
		 * ** !!! 状态代码为16进制 !!! ***
		 * 1. MT模式下的状态 -> 处理方式
		 *	08, 10 START/REP-START已经发送 -> 发送SLA+W
		 *  18, 28 SLA_ACK / DATA_ACK    -> 发送数据，如果全部数据都发送了，发送STOP，通知应用
		 *  20, 30 SLA_NACK / DATA_NACK  -> 发送STOP，通知应用
		 *  38 总线仲裁失败                 -> 等待总线空闲时再发送start
		 */
		if( status == 0x08 || status == 0x10 ){
			uint8_t sla_w = ( twi.target << 1 ) + TW_WRITE;
			TWDR = sla_w;
			TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
			goto waitnext;
		}
		else if( status == 0x18 || status == 0x28 ){
			if( twi.cnt == 0 && twi.buf.w == NULL ){ //全部数据已经发送
				twi.buf.w = NULL;
				TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO);
				goto notice;
			}
			TWDR = VOID_DATA( twi.buf.w, uint8_t );
			++twi.buf.w;
			--twi.cnt;
			TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
			goto waitnext;
		}
		else if( status == 0x20 || status == 0x30){
			TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO);
			goto notice;
		}
		else if( status == 0x38 ){
			TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTA);
			goto waitnext;
		}
	}
	/*
	 * ** !!! 状态代码为16进制 !!! ***
	 * 2. SR模式下的状态 -> 处理方式
	 *  60, 68, 70, 78收到了自己的SLA+W / 广播 -> 开始接收数据
	 *  80, 90 收到数据 -> 正常接收数据
	 *  88, 98 收到的是最后一个数据 -> 接收数据，然后停止传输，通知应用
	 *  A0 收到主机的STOP/REPSTART -> 停止传输，通知应用
	 */
	else if( twi.mode == TwiModeSR )
	{
		if( status == 0x60 || status == 0x68 || status == 0x70 || status == 0x78 ){
			if( twi.cnt <= 1 ){
				TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
			}
			else{
				TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE);
			}
		}
		else if( status == 0x80 || status == 0x90 ){
			VOID_DATA( twi.buf.r, uint8_t ) = TWDR;
			++twi.buf.r;
			--twi.cnt;
			if( twi.cnt <= 0 ){  //指定长度的数据都读到了
				TWCR = _BV(TWINT) | _BV(TWEN);
				goto notice;
			}
			if( twi.cnt == 1){
				TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE);
			}
			else{
				TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE);
			}
			goto waitnext;
		}
		else if( status == 0x88 || status == 0x98 ){
			if( twi.cnt > 0 ){
				VOID_DATA( twi.buf.r, uint8_t ) = TWDR;
				++twi.buf.r;
				--twi.cnt;
			}
			TWCR = _BV(TWINT) | _BV(TWEN);
			goto notice;
		}
		else if( status == 0xA0 ){
			TWCR = _BV(TWINT) | _BV(TWEN);
			goto notice;
		}
	}
	waitnext:
	Epilog();
	return;
	notice:
	twiCancelTimeout();
	AvrXIntSendMessage( &twi.q, &twi.msg.u.mcb );
	Epilog();
	return;
}

static void twiTimeout( void )
{
	TWCR = 0;
	twi.mode = TwiModeStop;
}
void twi_set_timeout( unsigned to )
{
	timeout = to;
}

